package hell.satan.droidbdf.utils;

import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;

/**
 * 全局处理未经捕获的异常
 * handle cases where a thread is being terminated by an uncaught exception.
 *
 * @author Cerberus
 */
@SuppressWarnings("unused")
public class CrashHandler implements UncaughtExceptionHandler {
    /**
     * 崩溃日志文件格式
     * File name format of crash log name
     */
    private static final String FMT_CRASH_LOG = "crash_%1$tY%1$tm%1$td%1$tH%1$tM%1$tS.txt";

    private static CrashHandler sInstance;

    private final Context mContext;
    private final String mFolder;

    private UncaughtExceptionHandler mDefaultHandler;

    /**
     * 初始化CrashHandler实例
     * Initialize CrashHandler instance
     *
     * @param app    唯一的Application对象
     *               The only Application instance of a process
     * @param folder 存储Crash日志的目录
     *               Folder for storing Crash logs
     */
    public static void init(Application app, String folder) {
        synchronized (CrashHandler.class) {
            if (sInstance == null) {
                sInstance = new CrashHandler(app, folder);
            }
        }
    }

    /**
     * 处理已经被捕获的异常，可用于catch语句块中
     * Handle has been catched by the exception, available in the catch block
     *
     * @param tr 被捕获的异常
     *           The exception that was catched
     * @see #caughtException(Thread, Throwable)
     */
    public static void caughtException(Throwable tr) {
        caughtException(Thread.currentThread(), tr);
    }

    /**
     * 处理已经被捕获的异常，可用于catch语句块中
     * Handle has been captured by the exception, available in the catch block
     * <pre>
     * try {
     *     ...
     * } catch(Exception ex) {
     *     CrashHandler.caughtException(Thread.currentThread(), ex);
     * }
     * </pre>
     *
     * @param thread 产生异常的线程
     *               the thread that has a catched exception
     * @param tr     被捕获的异常
     *               the exception that was catched
     */
    public static void caughtException(Thread thread, Throwable tr) {
        if (sInstance != null) {
            sInstance.handleImpl(thread, tr, true);
        } else {
            Log.e("CrashHandler", "caughtException", tr);
        }
    }

    private CrashHandler(Application app, String folder) {
        mContext = app;
        mFolder = folder;
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread thread, Throwable tr) {
        handleImpl(thread, tr, true);
        if (mDefaultHandler != null) mDefaultHandler.uncaughtException(thread, tr);
    }

    private void handleImpl(Thread thread, Throwable tr, boolean dealed) {
        String name = String.format(FMT_CRASH_LOG, System.currentTimeMillis());
        File file = new File(mFolder, name);
        PrintWriter pw = null;
        try {
            pw = new PrintWriter(file);
            handleCommon(pw);
            handlePairs(pw, "thread", thread);//线程信息
            handlePairs(pw, "dealed", dealed);
            handleException(pw, tr);
        } catch (Throwable ignored) {
        } finally {
            try {
                if (pw != null) pw.close();
            } catch (Throwable ignored) {
            }
        }
    }

    private void handleCommon(PrintWriter pw) {
        try {
            PackageManager pm = mContext.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), 0);
            handlePairs(pw, "package", pi.packageName);
            handlePairs(pw, "vername", pi.versionName);
            handlePairs(pw, "vercode", pi.versionCode);

            handlePairs(pw, "brand", Build.BRAND);//品牌
            handlePairs(pw, "manufacturer", Build.MANUFACTURER);//厂商
            handlePairs(pw, "product", Build.PRODUCT);//产品名
            handlePairs(pw, "device", Build.DEVICE);//设备
            handlePairs(pw, "board", Build.BOARD);//主板
            handlePairs(pw, "release", Build.VERSION.RELEASE);//发布版本号
            handlePairs(pw, "sdk", Build.VERSION.SDK_INT);//sdk版本
        } catch (Throwable ignored) {
        }
    }

    private void handleException(PrintWriter pw, Throwable tr) {
        Throwable throwable = tr;
        do {
            throwable.printStackTrace(pw);
            throwable = throwable.getCause();
        } while (throwable != null);
    }

    private void handlePairs(PrintWriter pw, String key, Object value) throws IOException {
        pw.write(key + "=" + value + "\n");
    }

    @Override
    protected void finalize() throws Throwable {
        if (mDefaultHandler != null) Thread.setDefaultUncaughtExceptionHandler(mDefaultHandler);
        mDefaultHandler = null;
        super.finalize();
    }
}
